package com.sun.springbootfiledownload.utils;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import com.sun.springbootfiledownload.result.AliyunOssResult;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * 阿里云OSS工具类
 */
@Slf4j
@Component
public class AliyunOssPlusUtil {

    @Value("${aliyun.oss.endpoint}")
    private String endpoint;

    @Value("${aliyun.oss.access-key-id}")
    private String accessKeyId;

    @Value("${aliyun.oss.access-key-secret}")
    private String accessKeySecret;

    @Value("${aliyun.oss.bucket-name}")
    private String bucketName;

    @Value("${aliyun.oss.expire-time}")
    private long expireTime;

    /**
     * 获取OSS客户端实例
     *
     * @return OSS客户端
     */
    private OSS getOssClient() {
        return new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    }

    /**
     * 关闭OSS客户端
     *
     * @param ossClient OSS客户端实例
     */
    private void shutdownOssClient(OSS ossClient) {
        if (ossClient != null) {
            ossClient.shutdown();
        }
    }

    /**
     * 上传文件到OSS
     *
     * @param file 文件对象
     * @return 上传结果
     */
    public AliyunOssResult upload(MultipartFile file) {
        return upload(file, null);
    }

    /**
     * 上传文件到OSS指定目录
     *
     * @param file 文件对象
     * @param dir  目标目录(不以/开头，以/结尾，例如 "images/")
     * @return 上传结果
     */
    public AliyunOssResult upload(MultipartFile file, String dir) {
        OSS ossClient = getOssClient();
        try {
            // 获取文件名并生成唯一文件名
            String originalFilename = file.getOriginalFilename();
            String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
            String fileName = UUID.randomUUID().toString() + fileExtension;

            // 构建完整路径
            String fullPath = (dir == null || dir.trim().isEmpty()) ? fileName : dir + fileName;

            // 自动创建目录
            if (dir != null && !dir.trim().isEmpty() && !directoryExists(ossClient, dir)) {
                createDirectory(ossClient, dir);
            }

            // 上传文件
            ossClient.putObject(bucketName, fullPath, new ByteArrayInputStream(file.getBytes()));

            // 构建文件URL
            String fileUrl = "https://" + bucketName + "." + endpoint + "/" + fullPath;

            return AliyunOssResult.success(fileUrl);
        } catch (IOException e) {
            e.printStackTrace();
            return AliyunOssResult.fail("文件上传失败: " + e.getMessage());
        } finally {
            shutdownOssClient(ossClient);
        }
    }


    public AliyunOssResult uploadTemp(MultipartFile file, String dir) {
        if (file == null || file.isEmpty()) {
            return AliyunOssResult.fail("上传文件不能为空");
        }

        OSS ossClient = null;
        File tempFile = null;
        try {
            ossClient = getOssClient();

            // 创建临时文件
            tempFile = File.createTempFile("upload-", file.getOriginalFilename());
            file.transferTo(tempFile);

            // 获取文件名并生成唯一文件名
            String originalFilename = file.getOriginalFilename();
            String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
            String fileName = UUID.randomUUID().toString() + fileExtension;

            // 构建完整路径
            String fullPath = (dir == null || dir.trim().isEmpty()) ? fileName : dir + fileName;

            // 自动创建目录
            if (dir != null && !dir.trim().isEmpty() && !directoryExists(ossClient, dir)) {
                createDirectory(ossClient, dir);
            }

            // 上传文件
            ossClient.putObject(bucketName, fullPath, tempFile);

            // 构建文件URL
            String fileUrl = "https://" + bucketName + "." + endpoint + "/" + fullPath;

            return AliyunOssResult.success(fileUrl);
        } catch (IOException e) {
            log.error("文件上传失败", e);
            return AliyunOssResult.fail("文件上传失败: " + e.getMessage());
        } finally {
            shutdownOssClient(ossClient);
            if (tempFile != null) {
                tempFile.delete(); // 确保临时文件被删除
            }
        }
    }

    /**
     * 安全上传方法 - 完全避免临时文件依赖
     *
     * @param fileBytes        文件字节数组
     * @param originalFilename 原始文件名
     * @param folder           目标文件夹
     * @return 文件URL
     */
    public String safeUpload(byte[] fileBytes, String originalFilename, String folder) {
        OSS ossClient = getOssClient();
        try (ByteArrayInputStream inputStream = new ByteArrayInputStream(fileBytes)) {
            String filePath = folder + "/" + generateUniqueFileName(originalFilename);

            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(fileBytes.length);
            metadata.setContentType(detectContentType(originalFilename));

            PutObjectResult result = ossClient.putObject(bucketName, filePath, inputStream, metadata);
            log.info("文件上传成功: {}, ETag: {}", filePath, result.getETag());

            return generateFileUrl(filePath);
        } catch (Exception e) {
            log.error("文件上传失败: {}", originalFilename, e);
            throw new RuntimeException("OSS上传失败", e);
        }
    }

    private String detectContentType(String filename) {
        // 根据文件名返回对应的ContentType
        String extension = filename.substring(filename.lastIndexOf(".")).toLowerCase();
        switch (extension) {
            case ".xlsx":
                return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
            case ".jpg":
            case ".jpeg":
                return "image/jpeg";
            case ".png":
                return "image/png";
            default:
                return "application/octet-stream";
        }
    }

    /**
     * 判断目录是否存在
     *
     * @param dirPath 目录路径(不以/开头，以/结尾)
     * @return 是否存在
     */
    public boolean directoryExists(String dirPath) {
        OSS ossClient = getOssClient();
        try {
            return directoryExists(ossClient, dirPath);
        } finally {
            shutdownOssClient(ossClient);
        }
    }

    /**
     * 内部方法 - 判断目录是否存在
     *
     * @param ossClient OSS客户端实例
     * @param dirPath   目录路径
     * @return 是否存在
     */
    private boolean directoryExists(OSS ossClient, String dirPath) {
        // OSS中目录实际上是一个空对象，以/结尾
        if (!dirPath.endsWith("/")) {
            dirPath += "/";
        }

        // 列出指定前缀的对象
        ObjectListing objectListing = ossClient.listObjects(bucketName, dirPath);
        List<OSSObjectSummary> objectSummaries = objectListing.getObjectSummaries();

        // 如果存在以该目录路径为前缀的对象，则认为目录存在
        return !objectSummaries.isEmpty() || !objectListing.getCommonPrefixes().isEmpty();
    }

    /**
     * 创建目录
     *
     * @param dirPath 目录路径(不以/开头，以/结尾)
     * @return 是否创建成功
     */
    public boolean createDirectory(String dirPath) {
        OSS ossClient = getOssClient();
        try {
            return createDirectory(ossClient, dirPath);
        } finally {
            shutdownOssClient(ossClient);
        }
    }

    /**
     * 内部方法 - 创建目录
     *
     * @param ossClient OSS客户端实例
     * @param dirPath   目录路径
     * @return 是否创建成功
     */
    private boolean createDirectory(OSS ossClient, String dirPath) {
        if (!dirPath.endsWith("/")) {
            dirPath += "/";
        }

        // 在OSS中创建目录实际上是上传一个空对象，以/结尾
        InputStream emptyContent = new ByteArrayInputStream(new byte[0]);
        ossClient.putObject(bucketName, dirPath, emptyContent);

        return directoryExists(ossClient, dirPath);
    }

    /**
     * 下载文件
     *
     * @param filePath 文件路径(不包含bucket名称)
     * @return 文件流
     */
    public InputStream download(String filePath) {
        OSS ossClient = getOssClient();
        try {
            OSSObject ossObject = ossClient.getObject(bucketName, filePath);
            return ossObject.getObjectContent();
            // 注意：调用者需要负责关闭这个输入流
        } catch (Exception e) {
            shutdownOssClient(ossClient);
            throw new RuntimeException("文件下载失败: " + e.getMessage(), e);
        }
        // 注意：这里不关闭ossClient，因为需要保持流可用
    }

    /**
     * 删除文件
     *
     * @param filePath 文件路径
     * @return 是否删除成功
     */
    public boolean deleteFile(String filePath) {
        OSS ossClient = getOssClient();
        try {
            ossClient.deleteObject(bucketName, filePath);
            return true;
        } catch (Exception e) {
            return false;
        } finally {
            shutdownOssClient(ossClient);
        }
    }

    /**
     * 批量删除文件
     *
     * @param filePaths 文件路径列表
     * @return 删除成功的文件列表
     */
    public List<String> deleteFiles(List<String> filePaths) {
        OSS ossClient = getOssClient();
        List<String> successList = new ArrayList<>();
        try {
            for (String filePath : filePaths) {
                try {
                    ossClient.deleteObject(bucketName, filePath);
                    successList.add(filePath);
                } catch (Exception e) {
                    // 单个文件删除失败，继续处理其他文件
                }
            }
            return successList;
        } finally {
            shutdownOssClient(ossClient);
        }
    }

    /**
     * 删除指定文件夹下的所有文件
     *
     * @param dirPath 文件夹路径(不以/开头，以/结尾)
     * @return 删除的文件数量
     */
    public int deleteFilesInDirectory(String dirPath) {
        OSS ossClient = getOssClient();
        try {
            if (!dirPath.endsWith("/")) {
                dirPath += "/";
            }

            // 列出目录下所有文件
            List<String> fileKeys = new ArrayList<>();
            ObjectListing objectListing = ossClient.listObjects(bucketName, dirPath);
            for (OSSObjectSummary objectSummary : objectListing.getObjectSummaries()) {
                fileKeys.add(objectSummary.getKey());
            }

            // 批量删除
            if (!fileKeys.isEmpty()) {
                DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(bucketName)
                        .withKeys(fileKeys);
                ossClient.deleteObjects(deleteObjectsRequest);
            }

            return fileKeys.size();
        } finally {
            shutdownOssClient(ossClient);
        }
    }

    /**
     * 删除文件夹(包括文件夹本身)
     *
     * @param dirPath 文件夹路径(不以/开头，以/结尾)
     * @return 是否删除成功
     */
    public boolean deleteDirectory(String dirPath) {
        OSS ossClient = getOssClient();
        try {
            if (!dirPath.endsWith("/")) {
                dirPath += "/";
            }

            // 先删除目录下所有文件
            deleteFilesInDirectory(dirPath);

            // 删除目录本身(OSS中的目录实际上是一个空对象)
            ossClient.deleteObject(bucketName, dirPath);

            return true;
        } catch (Exception e) {
            return false;
        } finally {
            shutdownOssClient(ossClient);
        }
    }

    /**
     * 获取文件的临时URL
     *
     * @param filePath 文件路径
     * @return 临时URL
     */
    public String getTempUrl(String filePath) {
        OSS ossClient = getOssClient();
        try {
            // 设置URL过期时间
            Date expiration = new Date(System.currentTimeMillis() + expireTime * 1000);

            // 生成URL
            URL url = ossClient.generatePresignedUrl(bucketName, filePath, expiration);

            return url.toString();
        } finally {
            shutdownOssClient(ossClient);
        }
    }

    /**
     * 检查文件是否存在
     *
     * @param filePath 文件路径
     * @return 是否存在
     */
    public boolean fileExists(String filePath) {
        OSS ossClient = getOssClient();
        try {
            return ossClient.doesObjectExist(bucketName, filePath);
        } finally {
            shutdownOssClient(ossClient);
        }
    }


    /**
     * 大文件分片上传（兼容最新SDK版本）
     * @param localFile 本地文件
     * @param folder 目标文件夹
     * @return 文件访问URL
     */
    public String uploadLargeFile(File localFile, String folder) throws Exception {
        OSS ossClient = getOssClient();
        String objectKey = folder + "/" + localFile.getName();

        // 1. 初始化分片上传
        InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(bucketName, objectKey);
        InitiateMultipartUploadResult initResponse = ossClient.initiateMultipartUpload(initRequest);
        String uploadId = initResponse.getUploadId();

        // 2. 设置分片大小（20MB）
        final long partSize = 20 * 1024 * 1024;
        List<PartETag> partETags = new ArrayList<>();

        try (FileInputStream fis = new FileInputStream(localFile)) {
            // 3. 分片上传
            byte[] buffer = new byte[(int) partSize];
            int partNumber = 1;
            int bytesRead;

            while ((bytesRead = fis.read(buffer)) != -1) {
                // 修正点：使用新版本的请求构建方式
                UploadPartRequest uploadRequest = new UploadPartRequest();
                uploadRequest.setBucketName(bucketName);
                uploadRequest.setKey(objectKey);
                uploadRequest.setUploadId(uploadId);
                uploadRequest.setPartNumber(partNumber++);
                uploadRequest.setInputStream(new ByteArrayInputStream(buffer, 0, bytesRead));
                uploadRequest.setPartSize(bytesRead);

                UploadPartResult uploadResult = ossClient.uploadPart(uploadRequest);
                partETags.add(uploadResult.getPartETag());
            }

            // 4. 完成分片上传
            CompleteMultipartUploadRequest completeRequest = new CompleteMultipartUploadRequest(
                    bucketName, objectKey, uploadId, partETags);

            CompleteMultipartUploadResult completeResult =
                    ossClient.completeMultipartUpload(completeRequest);

            return generateFileUrl(objectKey);
        } catch (Exception e) {
            // 5. 出错时中止上传
            ossClient.abortMultipartUpload(
                    new AbortMultipartUploadRequest(bucketName, objectKey, uploadId));
            throw e;
        }
    }

    /**
     * 辅助方法：生成唯一文件名
     */
    private String generateUniqueFileName(String originalFilename) {
        String extension = "";
        if (originalFilename.contains(".")) {
            extension = originalFilename.substring(originalFilename.lastIndexOf("."));
        }
        return UUID.randomUUID().toString().replace("-", "") + extension;
    }

    /**
     * 辅助方法：格式化文件大小
     */
    private String formatSize(long bytes) {
        if (bytes < 1024) return bytes + "B";
        if (bytes < 1024 * 1024) return (bytes / 1024) + "KB";
        return (bytes / (1024 * 1024)) + "MB";
    }

    /**
     * 生成文件访问URL（基础版）
     *
     * @param filePath OSS中的文件路径（如 "documents/example.jpg"）
     * @return 可公开访问的URL
     */
    public String generateFileUrl(String filePath) {
        // 1. 直接拼接URL（简单但不安全，仅适用于公共读Bucket）
        return String.format("https://%s.%s/%s",
                bucketName,
                "oss-cn-hangzhou.aliyuncs.com", // 需替换为你的Endpoint
                filePath);
    }


    /**
     * 生成带签名的临时访问URL（安全推荐）
     *
     * @param filePath      OSS中的文件路径
     * @param expireSeconds URL有效期（秒）
     * @return 带签名的临时URL
     */
    public String generateFileUrl(String filePath, int expireSeconds) {
        OSS ossClient = getOssClient();
        // 1. 设置URL过期时间
        Date expiration = new Date(System.currentTimeMillis() + expireSeconds * 1000);

        // 2. 生成签名URL（自动处理特殊字符）
        return ossClient.generatePresignedUrl(
                bucketName,
                filePath,
                expiration,
                com.aliyun.oss.HttpMethod.GET).toString();
    }
}